package com.ctsi.ftp.util;


import java.io.*;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.StringTokenizer;
import java.util.zip.GZIPOutputStream;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.apache.commons.net.ftp.FTPClient;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.Path;
import sun.net.TelnetInputStream;
import sun.net.TelnetOutputStream;
import sun.net.ftp.FtpClient;
import sun.net.ftp.FtpProtocolException;

public class FtpUtilNew {
    private static Log log = LogFactory.getLog(FtpUtilNew.class);

    private String ip = "";

    private String username = "";

    private String password = "";

    private int port = -1;

    private String path = "";

    private static FtpClient ftpClient = null;

    private static String tail = "";

    private static int lineLimit = 1000;

    public FtpUtilNew(String serverIP, String username, String password) {
        this.ip = serverIP;
        this.username = username;
        this.password = password;
        this.tail = "</info>\r\n";
    }

    public FtpUtilNew(String serverIP, int port, String username, String password) {
        this.ip = serverIP;
        this.username = username;
        this.password = password;
        this.port = port;
        this.tail = "</info>\r\n";
    }

    public String getPath() {
        return path;
    }

    public void setPath(String path) {
        this.path = path;
    }

    /**
     * 连接ftp服务器
     *
     * @throws IOException
     */
    public boolean connectServer() {
        ftpClient = FtpClient.create();
        //创建地址
        SocketAddress addr = null;
        try {
            if (this.port != -1) {
                addr = new InetSocketAddress(this.ip, port);
                ftpClient.connect(addr); // .openServer(this.ip, this.port);
            } else {
//				ftpClient.openServer(this.ip);
                addr = new InetSocketAddress(this.ip, 21);
                ftpClient.connect(addr);
            }
            ftpClient = FtpClient.create();
            ftpClient.connect(addr);
            //登陆
            ftpClient.login(username, password.toCharArray());
//            ftpClient.login(username, null, password);
            if (this.path.length() != 0) {
                ftpClient.changeDirectory(this.path);// path是ftp服务下主目录的子目录
            }
            ftpClient.setBinaryType();  // 用2进制上传、下载
            System.out.println("已登录到\"" + ftpClient.getWorkingDirectory() + "\"目录");
            log.debug("已登录到\"" + ftpClient.getWorkingDirectory() + "\"目录");
            return true;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        } catch (FtpProtocolException e) {
            e.printStackTrace();
        }
        return true;
    }

    /**
     * 断开与ftp服务器连接
     *
     * @throws IOException
     */
    public boolean closeServer() {
        try {
            if (ftpClient != null) {
                ftpClient.close();
            }
            log.debug("已从服务器断开");
            return true;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 检查文件夹在当前目录下是否存在
     *
     * @param dir
     * @return
     */
    private boolean isDirExist(String dir) {
        String pwd = "";
        try {
            pwd = ftpClient.getWorkingDirectory();
            ftpClient.changeDirectory(dir);
            ftpClient.changeDirectory(pwd);
//			ftpClient.cd(pwd);
        } catch (Exception e) {
            return false;
        }
        return true;
    }


    /**
     * ftp上传 如果服务器段已存在名为filename的文件夹，该文件夹中与要上传的文件夹中同名的文件将被替换
     *
     * @param filename 要上传的文件（或文件夹）名
     * @return
     * @throws Exception
     */
    public boolean upload(String filename) {
        String newname = "";
        if (filename.indexOf("/") > -1) {
            newname = filename.substring(filename.lastIndexOf("/") + 1);
        } else {
            newname = filename;
        }
        return upload(filename, newname);
    }

    /**
     * ftp上传 如果服务器段已存在名为newName的文件夹，该文件夹中与要上传的文件夹中同名的文件将被替换
     *
     * @param fileName 要上传的文件（或文件夹）名
     * @param newName  服务器段要生成的文件（或文件夹）名
     * @return
     */
    public boolean upload(String fileName, String newName) {
        try {
            String savefilename = new String(fileName.getBytes("GBK"), "GBK");
            File file_in = new File(savefilename);// 打开本地待长传的文件
            if (!file_in.exists()) {
                throw new Exception("此文件或文件夹[" + file_in.getName() + "]有误或不存在!");
            }
            if (file_in.isDirectory()) {
                upload(file_in.getPath(), newName, ftpClient.getWorkingDirectory());
            } else {
                uploadFile(file_in.getPath(), newName);
            }

            return true;
        } catch (Exception e) {
            e.printStackTrace();
            log.error("Exception e in Ftp upload(): " + e.toString());
            return false;
        } finally {

        }
    }

    /**
     * 真正用于上传的方法
     *
     * @param fileName
     * @param newName
     * @param path
     * @throws Exception
     */
    private void upload(String fileName, String newName, String path)
            throws Exception {
        ftpClient.changeDirectory(path);//.cd(path);
        String savefilename = new String(fileName.getBytes("ISO-8859-1"), "GBK");
        File file_in = new File(savefilename);// 打开本地待长传的文件
        if (!file_in.exists()) {
            throw new Exception("此文件或文件夹[" + file_in.getName() + "]有误或不存在!");
        }
        if (file_in.isDirectory()) {
            ftpClient.changeDirectory(newName); //.cd(newName);
            File sourceFile[] = file_in.listFiles();
            for (int i = 0; i < sourceFile.length; i++) {
                if (!sourceFile[i].exists()) {
                    continue;
                }
                if (sourceFile[i].isDirectory()) {
                    this.upload(sourceFile[i].getPath(), sourceFile[i]
                            .getName(), path + "/" + newName);
                } else {
                    this.uploadFile(sourceFile[i].getPath(), sourceFile[i]
                            .getName());
                }
            }
        } else {
            uploadFile(file_in.getPath(), newName);
        }
    }

    /**
     * upload 上传文件
     *
     * @param filename 要上传的文件名
     * @param newname  上传后的新文件名
     * @return -1 文件不存在 >=0 成功上传，返回文件的大小
     * @throws Exception
     */
    public long uploadFile(String filename, String newname) throws Exception {
        long result = 0;
//		TelnetOutputStream os = null;
        OutputStream os = null;
        FileInputStream is = null;
        try {
            File file_in = new File(filename);
            if (!file_in.exists())
                return -1;
            os = ftpClient.putFileStream(newname + "_bak"); //.put(newname+"_bak");
            result = file_in.length();
            is = new FileInputStream(file_in);
            byte[] bytes = new byte[1024];
            int c;
            while ((c = is.read(bytes)) != -1) {
                os.write(bytes, 0, c);
            }
        } finally {
            if (is != null) {
                is.close();
            }
            if (os != null) {
                os.close();
            }
        }
        ftpClient.rename(newname + "_bak", newname);
        return result;
    }

    /**
     * 从ftp下载文件到本地
     *
     * @param filename    服务器上的文件名
     * @param newfilename 本地生成的文件名
     * @return
     * @throws Exception
     */
    public long downloadFile(String filename, String newfilename) {
        long result = 0;
//		TelnetInputStream is = null;
        InputStream is = null;
        FileOutputStream os = null;
        try {
            is = ftpClient.getFileStream(filename); //.get(filename);
            File outfile = new File(newfilename);
            os = new FileOutputStream(outfile);
            byte[] bytes = new byte[1024];
            int c;
            while ((c = is.read(bytes)) != -1) {
                os.write(bytes, 0, c);
                result = result + c;
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (FtpProtocolException e) {
            e.printStackTrace();
        } finally {
            try {
                if (is != null) {
                    is.close();
                }
                if (os != null) {
                    os.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return result;
    }

    /**
     * 取得相对于当前连接目录的某个目录下所有文件列表
     *
     * @param path
     * @return
     */
    public List getFileList(String path) {
        List list = new ArrayList();
        DataInputStream dis;
        try {
            dis = new DataInputStream(ftpClient.nameList(this.path + path));
            String filename = "";
            while ((filename = dis.readLine()) != null) {
                list.add(filename);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (FtpProtocolException e) {
            e.printStackTrace();
        }
        return list;
    }

    /**
     * upload 上传文件
     *
     * @param filename
     * @return -1 文件不存在 >=0 成功上传，返回文件的大小
     * @throws Exception
     */
    public long uploadFile(String filename) throws Exception {
        long result = 0;
//		TelnetOutputStream os = null;
        OutputStream os = null;
        FileInputStream is = null;
        try {
            File file_in = new File(filename);
            if (!file_in.exists())
                return -1;
            os = ftpClient.putFileStream(file_in.getName()); //.put(file_in.getName());
            result = file_in.length();
            is = new FileInputStream(file_in);
            byte[] bytes = new byte[1024];
            int c;
            while ((c = is.read(bytes)) != -1) {
                os.write(bytes, 0, c);
            }
        } finally {
            if (is != null) {
                is.close();
            }
            if (os != null) {
                os.close();
            }
        }
        return result;
    }

    /**
     * upload 上传文件
     *
     * @return -1 文件不存在 >=0 成功上传，返回文件的大小
     * @throws Exception
     */
    public long uploadFile(File file_in) throws Exception {
        long result = 0;
//		TelnetOutputStream os = null;
        OutputStream os = null;
        FileInputStream is = null;
        try {
            if (!file_in.exists())
                return -1;
            os = ftpClient.putFileStream(file_in.getName() + "_bak"); //.put(file_in.getName()+"_bak");
            result = file_in.length();
            is = new FileInputStream(file_in);
            byte[] bytes = new byte[1024];
            int c;
            while ((c = is.read(bytes)) != -1) {
                os.write(bytes, 0, c);
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("uploadFile[" + file_in.getPath() + "] error");
            throw new Exception(e);
        } finally {

            if (is != null) {
                is.close();
            }
            if (os != null) {
                os.close();
            }
        }
        ftpClient.rename(file_in.getName() + "_bak", file_in.getName());
        return result;
    }

    public long uploadFile(String filename, String newname, String path) {
        try {
            ftpClient.changeDirectory(path); //.cd(path);
        } catch (IOException e) {
            // TODO 自动生成 catch 块
            e.printStackTrace();
        } catch (FtpProtocolException e) {
            e.printStackTrace();
        }
        return 0l;
    }


    public static boolean uploadFileByDirectory(String url, int port, String user
            , String password, String remotePath, File[] files, String successLocalPath, String filePostfix) throws Exception {

        FtpUtilNew ftp = new FtpUtilNew(url, port, user, password);
        ftp.setPath(remotePath);
        ftp.connectServer();
        String fileName = "";
        for (File file : files) {
            if (file == null) {
                continue;
            }
            fileName = file.getName();
            if (filePostfix != null) {
                if (!filePostfix.equals(".*")) {
                    if (!fileName.endsWith(filePostfix)) {
                        continue;
                    }
                }
            }

            long ret = ftp.uploadFile(file);
            if (ret >= 0) {
                File newFilePath = new File(successLocalPath);
                if ((!newFilePath.exists()) || (!newFilePath.isDirectory())) {
                    //如果,待放上传成功的文件的目录不存在,则创建一个新目录
                    newFilePath.mkdir();
                }
                file.renameTo(new File(successLocalPath + File.separator + file.getName()));
                file.delete();
            }
        }

        ftp.closeServer();
        return true;
    }

    public static boolean uploadFileByDirectory(String ftpUrl, int ftpPort, String ftpUserName
            , String ftpPassword, String remotePath, Path[] files, String successHdfsPath, String filePostfix, boolean isGZip) throws Exception {

        boolean success = false;
        FtpUtilNew ftp = new FtpUtilNew(ftpUrl, ftpPort, ftpUserName, ftpPassword);
        ftp.setPath(remotePath);
        ftp.connectServer();
        String fileName = "";
        String gzFileName="";
        try {
            Path newFilePath = new Path(successHdfsPath);
            if((!HdfsUtil.existsHdfsPath(newFilePath))||(!HdfsUtil.isHdfsDirectory(newFilePath))){
                //如果,待放上传成功的文件的目录不存在,则创建一个新目录
                HdfsUtil.makeHdfsDir(newFilePath);
            }

            for( Path file:files){
                if (file==null){
                    continue;
                }
                if (HdfsUtil.isHdfsFile(file)) {
                    // 遍历待上传的文件
                    fileName = file.getName();
                    if(filePostfix!=null){
                        if(!filePostfix.equals(".*")){
                            if(!fileName.endsWith(filePostfix)){
                                log.info("==============Not a specified file ========"+fileName);
                                return true;
                            }
                        }
                    }
                    // 上传文件
                    log.debug("==============begin upload file ========"+fileName);
                    if(isGZip){
//								将文件拆分
                        //按行读取输入文件
                        FSDataInputStream in=HdfsUtil.ReadHDFSFileStream(file);
                        BufferedReader lineRead=new BufferedReader(new InputStreamReader(in,"UTF-8"));
                        //通过路径获得文件类型，判断文件输出类型（LUA或NUA）
                        String[] fileParent=file.getParent().toString().split("/");
                        String fileType=fileParent[5].substring(1);
                        String fileNamePre="";
                        if(fileType.contains("NetPub")){
                            fileNamePre="LUA";
                        }else{
                            fileNamePre="NUA";
                        }

                        String line="";
                        int temp =0;
                        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");

                        System.out.println("1---FTPUtil...Uploading the gzFile:"+gzFileName);
                        //SXS:file catch****************************************
                        ArrayList<byte[]> fileContent=new ArrayList<byte[]>();
                        String id="";
                        while((line=lineRead.readLine())!=null){
                            temp++;
                            line+="\r\n";
                            fileContent.add(line.getBytes());
                            if(temp==lineLimit){
                                id= FileIdGenerator.getId();
                                gzFileName=fileNamePre +"_" + id + "_"+ sdf.format(new Date()) + ".xml.gz";
                                uploadGzFile(ftpClient, gzFileName, id, fileContent, temp);
                                fileContent.clear();
                                temp=0;
                            }
                        }
                        if(temp!=0){
                            id= FileIdGenerator.getId();
                            gzFileName=fileNamePre +"_" + id + "_"+ sdf.format(new Date()) + ".xml.gz";
                            uploadGzFile(ftpClient, gzFileName, id, fileContent, temp);
                            fileContent.clear();
                        }
                    }
                }

                // 将上传成功的文件转移到新目录中
                log.debug("==============begin move file ========"+fileName);
                HdfsUtil.renameHdfsFile(file,new Path(successHdfsPath+"/"+file.getName()));
                log.debug("==============end move file ========"+fileName);
                success = true;
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception("上传文件["+fileName+"|"+gzFileName+"]中的文件出错!");
        }

        ftp.closeServer();
        return success;
    }

    private static OutputStream uploadGzFileStream_head(FtpClient ftpClient, String newName, byte[] buf) throws Exception {
        // TODO Auto-generated method stub
        OutputStream out;
        out = ftpClient.putFileStream(newName);
        byte[] gzBuf = GzUtil.gZip(buf, buf.length);
        out.write(gzBuf, 0, gzBuf.length);
        return out;
    }

    private static void uploadGzFileStream_tail(OutputStream out, FtpClient ftpClient, String newName, byte[] buf) throws Exception {
        // TODO Auto-generated method stub

        byte[] gzBuf = GzUtil.gZip(buf, buf.length);
        out.write(gzBuf, 0, gzBuf.length);
        out.close();
    }

    public static void appendGzFileStream(OutputStream out, FtpClient ftpClient, String newName, byte[] buf) throws Exception {

        byte[] gzBuffer = GzUtil.gZip(buf, buf.length);
        out.write(gzBuffer, 0, gzBuffer.length);

    }

    private static void uploadGzFile (FtpClient ftp, String newName, String id, ArrayList<byte[]> content, int lineNum ) throws Exception{
        OutputStream out = ftp.putFileStream(newName);
        //add the xml file head
        String head="<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"
                + "<info id=\""+ id + "\" type=\"nat_cdr_info\" resultnum=\"" + lineNum +"\">\r\n";
        byte[] gzBuffer=GzUtil.gZip(head.getBytes(),head.getBytes().length);
        out.write(gzBuffer, 0, gzBuffer.length);
        //wirte the file content

        for(byte[] buf : content){
            gzBuffer=GzUtil.gZip(buf,buf.length);
            out.write(gzBuffer, 0, gzBuffer.length);
        }
        //add the xml file tail
//        String tail="</info>\r\n";
        gzBuffer=GzUtil.gZip(tail.getBytes(),tail.getBytes().length);
        out.write(gzBuffer, 0, gzBuffer.length);
        //close the outputstream
        out.close();

        //reanme after uploading successfully
        ftp.rename(newName, newName+".ok");
//        if(!ret) throw new Exception("重命名文件["+newName+"]时出错");
    }
}
